
package com.zy.je.common.core.utils.reflection;

import com.zy.je.common.core.exception.BizException;
import com.zy.je.common.core.utils.CheckUtil;
import com.zy.je.common.core.utils.lang.StringUtil;
import com.zy.je.common.core.web.rest.ResultStatus;
import org.springframework.util.Assert;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @author zy
 */
public class Reflection {
    private static final Pattern GET_PATTERN = Pattern.compile("^get[A-Z].*");
    private static final Pattern IS_PATTERN = Pattern.compile("^is[A-Z].*");

    private Reflection() {
    }

    public static String fnToFieldName(Fn fn) {
        try {
            Method method = fn.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);
            String getter = serializedLambda.getImplMethodName();
            if (GET_PATTERN.matcher(getter).matches()) {
                getter = getter.substring(3);
            } else if (IS_PATTERN.matcher(getter).matches()) {
                getter = getter.substring(2);
            }
//            return Introspector.decapitalize(getter);
            return StringUtil.uncapitalize(getter);
        } catch (ReflectiveOperationException e) {
            throw new BizException(ResultStatus.BUSINESS_ERROR, e.getMessage());
        }
    }

    public static String[] fnsToFieldNameAarray(Fn<?, Object>... fns) {
        if (null == fns || fns.length <= 0) {
            return new String[0];
        }
        List<String> properties = new ArrayList<>();
        for (Fn<?, Object> fn : fns) {
            properties.add(Reflection.fnToFieldName(fn));
        }
        return properties.toArray(new String[properties.size()]);
    }

    public static Field getFieldWithAccessible(Class<?> clazz, String fieldName) throws NoSuchFieldException {
        if (CheckUtil.isBlank(fieldName)) {
            throw new NoSuchFieldException("The argument [fieldName] is required");
        }
        for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return getFieldFromCurrentClassWithAccessible(superClass, fieldName);
            } catch (NoSuchFieldException e) {
                //如果属性是在父类中，异常到这里是正常的
            }
        }
        throw new NoSuchFieldException("No such field: " + clazz.getName() + '.' + fieldName);
    }

    private static Field getFieldFromCurrentClassWithAccessible(Class<?> clazz, String fieldName)
            throws NoSuchFieldException {
        Field field = clazz.getDeclaredField(fieldName);
        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        return field;
    }

    public static <T> void setValue(Object source, Object target, String fieldName)
            throws NoSuchFieldException, IllegalAccessException {
        Assert.notNull(source, "The argument [source] is required");
        Assert.notNull(target, "The argument [fieldName] is required");
        Assert.notNull(fieldName, "The argument [fieldName] is required");

        Field srcField = Reflection.getFieldWithAccessible(source.getClass(), fieldName);
        Field targetField = Reflection.getFieldWithAccessible(target.getClass(), fieldName);
        targetField.set(target, srcField.get(source));
    }
}
